package com.jxin.mall.member.manager.impl;

import com.jxin.mall.config.parent.BaseService;
import com.jxin.mall.entity.consts.HttpRspStatusEnum;
import com.jxin.mall.entity.entity.GlobalRsp;
import com.jxin.mall.member.consts.LoginTypeEnum;
import com.jxin.mall.member.consts.RedisKeyConsts;
import com.jxin.mall.member.consts.ServiceLogMsgConsts;
import com.jxin.mall.member.domain.consts.AllianceTypeEnum;
import com.jxin.mall.member.domain.consts.MemberUserFileEnum;
import com.jxin.mall.member.domain.entity.LoginHistory;
import com.jxin.mall.member.domain.entity.MemberUser;
import com.jxin.mall.member.domain.exception.MemberUserServiceException;
import com.jxin.mall.member.domain.service.LoginHistoryService;
import com.jxin.mall.member.domain.service.MemberUserService;
import com.jxin.mall.member.exception.LoginRegisterServiceException;
import com.jxin.mall.member.feign.WxAccessServiceFeign;
import com.jxin.mall.member.feign.WxRedisKeyServiceFeign;
import com.jxin.mall.member.manager.LoginRegisterManager;
import com.jxin.mall.member.service.entity.dto.req.UserLoginReqDTO;
import com.jxin.mall.member.service.entity.dto.req.UserRegisterReqDTO;
import com.jxin.mall.middle.util.redis.RedisDataSourceTransactionUtil;
import com.jxin.mall.middle.util.redis.RedisUtil;
import com.jxin.mall.weixin.service.entity.dto.rsp.WxUserRspDTO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.TransactionStatus;

import javax.annotation.Resource;



/**
 * 会员登入注册管理类实现类
 * @author  蔡佳新
 * @version 1.0
 * @since   jdk 1.8
 */
@Service("loginRegisterManager")
@Slf4j
public class LoginRegisterManagerImpl  extends BaseService implements LoginRegisterManager  {

    @Resource
    private WxRedisKeyServiceFeign wxRedisKeyServiceFeign;
    @Resource
    private WxAccessServiceFeign wxAccessServiceFeign;
    @Resource
    private MemberUserService memberUserService;
    @Resource
    private LoginHistoryService loginHistoryService;
    @Resource(name = "mutilStrProtoRedisUtil")
    private RedisUtil mutilStrProtoRedisUtil;
    @Resource(name = "strProtoRedisUtil")
    private RedisUtil strProtoRedisUtil;
    /**
     * 会员注册,根据手机号码
     * @param  userRegisterReqDTO 注册参数
     * @return 全局响应参数
     * @throws LoginRegisterServiceException 联合登入,但获取不到用户信息
     * @author 蔡佳新
     */
    @Override
    public GlobalRsp registerByPhone(UserRegisterReqDTO userRegisterReqDTO) {
        /*
         * 检查手机号码存在校验
         */
        final GlobalRsp existVerify = isExistVerify(userRegisterReqDTO.getTel(),
                                                    MemberUserFileEnum.PHONE);
        if (existVerify != null) {
            return existVerify;
        }
        /*
         * 校验注册码,不通过则不许注册
         */
        final GlobalRsp verifyCodeRsp = registerCodeVerify(userRegisterReqDTO.getTel(),
                                                           userRegisterReqDTO.getRegisterCode());
        if (verifyCodeRsp != null) {
            return verifyCodeRsp;
        }
        // 第三方联合注册
        final GlobalRsp allianceRegisterGlobalRsp = allianceRegister(userRegisterReqDTO);
        if(allianceRegisterGlobalRsp != null){
            return allianceRegisterGlobalRsp;
        }
        // 普通注册
        return creatUser(MemberUser.builder()
                                   .username(userRegisterReqDTO.getTel())
                                   .phone(userRegisterReqDTO.getTel())
                                   .password(userRegisterReqDTO.getPassword())
                                   .build());
    }

    /**
     * 会员注册,根据账号
     * @param  userRegisterReqDTO 注册参数
     * @return 全局响应参数
     * @author 蔡佳新
     */
    @Override
    public GlobalRsp registerByUsername(UserRegisterReqDTO userRegisterReqDTO) {
        /*
         * 检查账号是否已经存在
         */
        final GlobalRsp existVerify = isExistVerify(userRegisterReqDTO.getUsername(),
                                                    MemberUserFileEnum.USERNAME);
        if (existVerify != null) {
            return existVerify;
        }
        // 第三方联合注册
        final GlobalRsp allianceRegisterGlobalRsp = allianceRegister(userRegisterReqDTO);
        if(allianceRegisterGlobalRsp != null){
            return allianceRegisterGlobalRsp;
        }
        // 普通注册
        return creatUser(MemberUser.builder()
                                   .username(userRegisterReqDTO.getUsername())
                                   .phone(userRegisterReqDTO.getTel())
                                   .password(userRegisterReqDTO.getPassword())
                                   .build());
    }
    /**
     * 唯一登录
     * 1.插入新的登入历史记录
     * 2.失效当前启用的登入历史记录
     * 3.删除当前启用的登入历史记录的redis的token
     * @param  userLoginReqDTO 登入参数
     * @param  memberUser      用户实例
     * @return 当前登入的令牌
     * @author 蔡佳新
     */
    @Override
    public String onlyOneLogin(MemberUser memberUser, UserLoginReqDTO userLoginReqDTO) {
        /*
         * 唯一登录
         * 1.插入新的登入历史记录
         * 2.失效当前启用的登入历史记录
         * 3.删除当前启用的登入历史记录的redis的token
         */
        final String token;
        final TransactionStatus transactionStatus = RedisDataSourceTransactionUtil.begin();
        try {
            token = mutilStrProtoRedisUtil.createToken(RedisKeyConsts.LOGIN_TOKEN, memberUser.getUserId());
            final Integer loginType = LoginTypeEnum.getTypeByName(userLoginReqDTO.getLoginType());
            /*
             * 如果同登录类型的启用登入历史存在.
             * 失效登入历史,并清除对应token
             */
            final LoginHistory loginHistory = loginHistoryService
                    .findEnableLoginHistoryByUserIdAndloginType(memberUser.getUserId(), loginType);
            if(loginHistory != null){
                disableTokenAndHistory(loginHistory);
            }
            // 创建登入历史记录
            creatLoginHistory(userLoginReqDTO, memberUser.getUserId(), token, loginType);
            RedisDataSourceTransactionUtil.commit(transactionStatus);
        } catch (Exception e) {
            RedisDataSourceTransactionUtil.rollback(transactionStatus);
            throw new LoginRegisterServiceException(e);
        }
        return token;
    }
    /**
     * 失效登入历史,并清除对应token
     * @param  loginHistory 登录历史
     * @author 蔡佳新
     */
    public void disableTokenAndHistory(LoginHistory loginHistory) {
        final Boolean disableSuccess = loginHistoryService.disableLoginHistory(loginHistory.getLoginHistoryId());
        if(!disableSuccess){
            throw new LoginRegisterServiceException(LoginRegisterManagerMsg.DISABLE_LOGIN_HISTORY_ERR, loginHistory.getLoginHistoryId());
        }
        mutilStrProtoRedisUtil.removeToken(RedisKeyConsts.LOGIN_TOKEN, loginHistory.getToken());
    }
    /**
     * 检验注册码是否正确
     * @param  phone        电话
     * @param  registerCode 注册码
     * @return 如果注册码校验不通过,返回 {@link GlobalRsp}。通过则返回<code>null</code>
     */
    public GlobalRsp registerCodeVerify(String phone, String registerCode) {
        final GlobalRsp<String> memberRegisterCodeByPhone = wxRedisKeyServiceFeign.getMemberRegisterCodeByPhone(phone);
        if(memberRegisterCodeByPhone.getCode().equals(HttpRspStatusEnum.HTTP_RES_CODE_201.getCode())){
            return memberRegisterCodeByPhone;
        }
        if(!memberRegisterCodeByPhone.getData().equals(registerCode)){
            return createCustom(HttpRspStatusEnum.HTTP_RES_CODE_202.getCode(),
                    HttpRspStatusEnum.HTTP_RES_CODE_202.getName());
        }
        return null;
    }
    /**
     * 微信联合登入
     * @param  accessToken 微信公总号授权登入令牌
     * @param  loginType   登录类型
     * @return 微信用户实例
     * @author 蔡佳新
     */
    @Override
    public GlobalRsp<String> wxAllianceLogin(String accessToken, String loginType) {
        final GlobalRsp<WxUserRspDTO> globalRsp = wxAccessServiceFeign.getWxUser(accessToken);
        if(!isSuccess(globalRsp)){
            throw new LoginRegisterServiceException(LoginRegisterManagerMsg.GET_WX_USER_ERR, accessToken);
        }
        final WxUserRspDTO wxUserRspDTO = globalRsp.getData();
        final MemberUser memberUserByWxCode = memberUserService.getMemberUserByCode(wxUserRspDTO.getOpenId(), AllianceTypeEnum.WX);
        if(memberUserByWxCode != null){
            final String loginToken = onlyOneLogin(memberUserByWxCode, UserLoginReqDTO.builder()
                                                                                      .loginType(loginType)
                                                                                      .build());
            return createSuccessWithData(loginToken);
        }
        final String wxUserToken = strProtoRedisUtil.createToken(RedisKeyConsts.LOGIN_WX_USER_TOKEN, wxUserRspDTO.getOpenId());
        return createCustomWithData(HttpRspStatusEnum.HTTP_RES_CODE_206.getCode(),
                                    HttpRspStatusEnum.HTTP_RES_CODE_206.getName(),
                                    wxUserToken);
    }
    /**
     * 获取微信授权地址
     * @return 全局响应参数
     * @author 蔡佳新
     */
    @Override
    public GlobalRsp getWxAccessUrl(String backUrl) {
        return wxAccessServiceFeign.getAccessUrl(backUrl);
    }
    /**
     * 用户关联联合登录编码
     * @param  code         关联编码
     * @param  userId       用户Id
     * @param  allianceType 联合类型
     * @return 如果更新失败则返回 异常全局参数,成功则返回 <code>null</code>
     */
    @Override
    public GlobalRsp userRelevancyCode(String code,
                                       Integer userId,
                                       AllianceTypeEnum allianceType) {
        switch(allianceType) {
            case QQ:
                return relevancyQq(code, userId);
            case WX:
                return relevancyWx(code, userId);
            default:
                throw new MemberUserServiceException(LoginRegisterManagerMsg.OVER_ALLIANCETYPES_ERR, code, allianceType.getName());
        }

    }

    //***************************************private**common************************************************************
    /**
     * 如果存在第三方联合注册,则走联合注册.
     * 不存在则,不注册并返回 <code>null</code>
     * @param userRegisterReqDTO 注册参数
     * @return 如果不存在第三方联合注册,则返回 <code>null</code> 。反之返回全局响应参数
     * @author 蔡佳新
     */
    private GlobalRsp allianceRegister(UserRegisterReqDTO userRegisterReqDTO) {
        // 微信联合注册
        if(StringUtils.isNotBlank(userRegisterReqDTO.getWxToken())){
            final WxUserRspDTO wxUserRspDTO = strProtoRedisUtil.getTokenValue(RedisKeyConsts.LOGIN_WX_USER_TOKEN,
                    userRegisterReqDTO.getWxToken(),
                    WxUserRspDTO.class);
            if(wxUserRspDTO == null){
                throw new LoginRegisterServiceException(LoginRegisterManagerMsg.GET_WX_USER_BY_TOKEN_ERR,
                        userRegisterReqDTO.getWxToken());
            }
            return creatUser(MemberUser.builder()
                                       .username(userRegisterReqDTO.getTel())
                                       .phone(userRegisterReqDTO.getTel())
                                       .password(userRegisterReqDTO.getPassword())
                                       .wxCode(wxUserRspDTO.getOpenId())
                                       .picImg(wxUserRspDTO.getHeadImgUrl())
                                       .sex(wxUserRspDTO.getSex())
                                       .nickname(wxUserRspDTO.getNickname())
                                       .build());
        }
        // QQ联合注册
        if(StringUtils.isNotBlank(userRegisterReqDTO.getQqToken())){
            log.info("QQ的开发账号搞不到");
            /*do some*/
        }
        return null;
    }
    /**
     * 判断指定属性的数据是否已经存在
     * @param  value              参数
     * @param  memberUserFileEnum 属性枚举实例
     * @return 字段存在数据 返回 {@link GlobalRsp}, 否则返回 <code>null</code>
     */
    @Nullable
    private GlobalRsp isExistVerify(String value, MemberUserFileEnum memberUserFileEnum) {
        if(memberUserService.isExistByField(value, memberUserFileEnum)){
            return createCustom(HttpRspStatusEnum.HTTP_RES_CODE_204.getCode(),
                    HttpRspStatusEnum.HTTP_RES_CODE_204.getName());
        }
        return null;
    }
    /**
     * 创建用户
     * @param  memberUser 注册用户信息
     * @return {@link GlobalRsp}
     */
    private GlobalRsp creatUser(MemberUser memberUser) {
        try {
            memberUserService.creatMemberUser(memberUser);
        } catch (MemberUserServiceException e) {
            log.error(e.getMessage(), e);
            return createFaild();
        }
        return createSuccess();
    }
    /**
     * 判断code是否已经关联
     * @param  code         编码
     * @param  allianceType 联合类型
     * @return 已存在关联对象返回 <code>true</code>
     * @author 蔡佳新
     */
    private boolean isExistForCode(String code, AllianceTypeEnum allianceType) {
        final MemberUser memberUserByCode = memberUserService.getMemberUserByCode(code, allianceType);
        if (memberUserByCode != null) {
            return true;
        }
        return false;
    }
    //****************************************private**onlyOneLogin*****************************************************
    /**
     * 创建登入历史记录
     * @param  userLoginReqDTO 登入参数
     * @param  userId          用户id
     * @param  token           令牌
     * @param  loginType       登录类型
     * @throws LoginRegisterServiceException  创建失败
     * @author 蔡佳新
     */
    private void creatLoginHistory(UserLoginReqDTO userLoginReqDTO,
                                   Integer userId,
                                   String token,
                                   Integer loginType) {
        final Boolean createSuccess = loginHistoryService
                .createLoginHistory(LoginHistory.builder()
                                                .userId(userId)
                                                .loginType(loginType)
                                                .addr(userLoginReqDTO.getAddr())
                                                .deviceInfor(userLoginReqDTO.getDeviceInfor())
                                                .token(token).build());
        if(!createSuccess){
            throw new LoginRegisterServiceException(LoginRegisterManagerMsg.CREATE_LOGIN_HISTORY_ERR, userId);
        }
    }

    //****************************************private**userRelevancyCode************************************************
    /**
     * 关联QQ的用户更新
     * @param code 编码
     * @param userId 用户id
     * @return 关联成功返回 <code>null</code>, 失败返回全局响应参数
     * @author 蔡佳新
     */
    private GlobalRsp relevancyQq(String code, Integer userId) {
        try {
            final Boolean updateSuccess = memberUserService.updateMemberUserByPrimaryKey(MemberUser.builder()
                                                                                                   .userId(userId)
                                                                                                   .qqCode(code)
                                                                                                   .build());
            if(!updateSuccess){
                throw new LoginRegisterServiceException(LoginRegisterManagerMsg.RELEVANCE_CODE_UPDATE_ERR, userId, code);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return createFaild();
        }
        return null;
    }

    /**
     * 关联微信的用户更新
     * @param code 编码
     * @param userId 用户id
     * @return 关联成功返回 <code>null</code>, 失败返回全局响应参数
     * @author 蔡佳新
     */
    private GlobalRsp relevancyWx(String code, Integer userId) {
        try {
            final Boolean updateSuccess = memberUserService.updateMemberUserByPrimaryKey(MemberUser.builder()
                                                                                                    .userId(userId)
                                                                                                    .wxCode(code)
                                                                                                    .build());
            if(!updateSuccess){
                throw new LoginRegisterServiceException(LoginRegisterManagerMsg.RELEVANCE_CODE_UPDATE_ERR, userId, code);
            }
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return createFaild();
        }
        return null;
    }

    //**************************************************logMsg**********************************************************
    /**
     * 日志信息常量的内部类
     * @author  蔡佳新
     * @version 1.0
     */
    private interface LoginRegisterManagerMsg {
        String DISABLE_LOGIN_HISTORY_ERR = ServiceLogMsgConsts.LOGIN_REGISTER_SERVICE + "用户登录记录失效异常,loginHistoryId:{}";
        String CREATE_LOGIN_HISTORY_ERR = ServiceLogMsgConsts.LOGIN_REGISTER_SERVICE + "新增登录记录异常,userId:{}";
        String GET_WX_USER_ERR = ServiceLogMsgConsts.LOGIN_REGISTER_SERVICE + "获取微信用户异常,token:{}";
        String GET_WX_USER_BY_TOKEN_ERR = ServiceLogMsgConsts.LOGIN_REGISTER_SERVICE + "联合登入,获取微信用户异常,token:{}";
        String RELEVANCE_CODE_UPDATE_ERR = ServiceLogMsgConsts.LOGIN_REGISTER_SERVICE + "联合登入,关联账号异常,userId:{}, code:{}";
        String OVER_ALLIANCETYPES_ERR = ServiceLogMsgConsts.LOGIN_REGISTER_SERVICE + " 关联类型超出范围。code:{},type:{}";
    }
}
